<?php
/**
 * Created by PhpStorm.
 * User: bolle
 * Date: 27.12.15
 * Time: 19:33
 */

namespace tbollmeier\alteraparser;


use tbollmeier\alteraparser\ast\AstNode;
use tbollmeier\alteraparser\ast\TextNode;

class Parser
{
    private $grammar;

    public function __construct($grammar)
    {
        $this->grammar = $grammar;
    }

    public function parse($inputStream)
    {
        $finder = new syntaxgraph\MatchFinder($inputStream);
        $this->grammar->getDockVertex()->walk($finder);

        if (!$finder->isStopped()) {
            return $this->createAst($finder->getPath());
        } else {
            $this->throwError($finder->getPath());
        }

    }

    public function parseString($data)
    {
        return $this->parse(new io\StringInputStream($data));
    }

    public function parseFile($filePath)
    {
        return $this->parseString(file_get_contents($filePath));
    }

    private function createAst($path)
    {
        $root = null;
        $stack = [];
        $text = '';

        foreach ($path as $segment) {
            $vertex = $segment[0];
            $ch = $segment[1];
            if ($vertex->isGroupStart()) {
                if ($text && count($stack) > 0) {
                    $parent = $stack[count($stack)-1];
                    $parent->addChild(new TextNode($text));
                }
                $text = '';
                $node = new AstNode([
                    'name' => $vertex->name,
                    'id' => $vertex->id
                ]);
                array_push($stack, $node);
            } elseif ($vertex->isGroupEnd()) {
                $node = array_pop($stack);
                $node->addChild(new TextNode($text));
                $text = '';
                $id = $node->id; // <-- save ID (must not be changed)
                $transformedNode = call_user_func($vertex->transformAstFn, $node);
                $transformedNode->id = $id;
                if (count($stack) > 0) {
                    $parent = $stack[count($stack)-1];
                    $parent->addChild($transformedNode);
                } else {
                    $root = $transformedNode;
                }
            }
            if ($ch !== null) {
                $text .= $ch;
            }
        }

        return $root;

    }

    private function throwError($path) {

        $parsed = '';
        foreach ($path as $segment) {
            $ch = $segment[1];
            if ($ch === null) continue;
            $parsed .= $ch;
        }

        throw new \Exception("Parser error after: {$parsed}");
    }

}